package pl.put.dcs.workManagerServer.deployer;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimerTask;
import org.apache.log4j.Logger;
import pl.put.dcs.core.utils.fileUtil.FileUtil;
import pl.put.dcs.workManagerServer.classLoaders.LibraryClassLoader;
import pl.put.dcs.workManagerServer.classLoaders.exceptions.LibraryClassLoaderException;
import pl.put.dcs.workManagerServer.config.ServerConfigManager;
import pl.put.dcs.workManagerServer.deployer.listeners.DeployListener;
import pl.put.dcs.workManagerServer.serviceConnectors.ControlModuleServerConnector;
import pl.put.dcs.workManagerServer.serviceConnectors.ControlModuleServerConnectorInterface;
import pl.put.model.project.Project;

// <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
// #[regen=yes,id=DCE.CA79A68D-9A74-66BF-A7B0-3304E48468B4]
// </editor-fold> 
public class Deployer extends TimerTask
{

    public static final Logger log = Logger.getLogger( Deployer.class.getName() );
    private static final Project.PROJECT_STATUS DEFAULT_NEW_STATUS = Project.PROJECT_STATUS.ACTIVE;

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.3D6C232C-A52A-D138-AF29-531AB2AD0D9A]
    // </editor-fold> 
    private DeployListener deployListener;
    private String projectsPath = "";

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.77B38F96-1058-2A8C-A8DD-C169420FA070]
    // </editor-fold> 
    public Deployer()
    {
        projectsPath += ServerConfigManager.getProjectDeployDir();
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,regenBody=yes,id=DCE.87B0E6E9-3517-46F2-98E4-4F4969EF9BF5]
    // </editor-fold> 
    public DeployListener getDeployListener()
    {
        return deployListener;
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,regenBody=yes,id=DCE.62D1D9DF-913E-3B84-065A-E251063FEE49]
    // </editor-fold> 
    public void addDeployListener( DeployListener val )
    {
        this.deployListener = val;
    }

    /**
     * Deploy-owanie wszystkich projektow, ktore sa w katalogu
     */
    public void fullDeploy()
    {
        deployProjects( false );
    }

    /**
     * Deploy-owanie tylko nowych projetkow
     */
    public void deployNewProjects()
    {
        deployProjects( true );
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
    // #[regen=yes,id=DCE.CAFA6CA4-9760-1D66-5185-F951464BF02C]
    // </editor-fold> 
    private void deployProjects( boolean onlyNewProjects )
    {
        
        ControlModuleServerConnectorInterface ctrlModule = ControlModuleServerConnector.getInstance();
        // pobranie juz zaladowanych jarow. Nazwa jara jest nazwa projektu (nie obchodzi nas wersja). Sprawdzamy date jara,
        // zeby sprawdzic czy przypadkiem nie jest to nowsza wersja. W takim wypadku wyswietlamy error z ta inforamcja
        List< Project > projectList = ctrlModule.getProjectList();
        log.debug( "Found " + projectList.size() + " projects in database" );
        File[] files = FileUtil.getFilesInFolder( projectsPath );
        
        // zakladam, ze wbudwane w api iterownaie mapy jest szybsze niz robienie zagniezdzonych petli. Tworze mape pomocnicza
        Map< String, File > fileMap = new HashMap< String, File >();
        if ( files != null )
        {
            for ( File f : files )
            {
                if ( FileUtil.getExtension( f.getName() ).equals( "jar" ) )
                {
                    fileMap.put( f.getName(), f );
                }
            }
        }
        // wyrzucanie komukatu, ze jar zostal podmieniony
        for ( Project p : projectList )
        {
            String jarName = p.getJarName();
            if ( !fileMap.containsKey( jarName ) )
            {
                ctrlModule.removeProject( p );
            }
            else
            {
                Long mDate = fileMap.get( jarName ).lastModified();
                if ( mDate > p.getJarModificationDate() )
                {
                    log.error( "Projects " + p.getProjectName() + " jar file (" + jarName + ") has changed! This can cause posible errors!" );
                }
                fileMap.remove( p.getJarName() );
            }
            // jezeli nie mamy deployowac tylko nowych projektow (tj. deploy-ujemy wszystkie)
            if ( !onlyNewProjects )
            {
                deployProject( p );
            }
        }

        Set< String > keys = fileMap.keySet();
        //LibraryClassLoader classLoader = LibraryClassLoader.getInstance();
        int countNewJars = keys.size();
        if ( countNewJars > 0 )
        {
            log.info( "Found " + countNewJars + " new projects to deploy." );
        }
        for ( String key : keys )
        {
            // stworzenie obiektu opisujacego projekt
            File f =  fileMap.get( key );
            Project newProject = createProjectDefObject( f );
            // dodanie projektu do bazy
            newProject = ctrlModule.addProject( newProject );
            deployProject( newProject );

        }
        
    }

    private void deployProject( Project project )
    {
        try
        {
            // dodadanie jara z definicja projektu do classpath-a
            log.info( "Load project " + project.getJarName() + " to classpath." );
            File f = new File( projectsPath + "/" + project.getJarName() );
            log.info( "is file: " + f.isFile() );
            LibraryClassLoader.addJar( projectsPath + "/" + project.getJarName() );
            // notyfikacja workManagera, ze zostal utworzony nowy projekt i musi to obsluzyc
            deployListener.newProjectAdded( project );
        }
        catch ( LibraryClassLoaderException ex )
        {
            log.error( ex );
        }
    }


    private Project createProjectDefObject( File f )
    {
        Project newProject = new Project();
        String jarName = f.getName();
        String fileName = FileUtil.getFileNameWithoutExt( jarName );
        // @TODO trzeba zmienic to tak, zeby ladowal ta nazwe z deskryptora
        newProject.setProjectName( fileName );
        newProject.setJarName( jarName );
        newProject.setJarModificationDate( f.lastModified() );
        newProject.setStatus( DEFAULT_NEW_STATUS );
        return newProject;
    }

    @Override
    public void run()
    {
        log.debug( "deployer start" );
        deployNewProjects();
        log.debug(  "deployer end" );
    }

}

